#ifndef GEDGEE_X64_H
#define GEDGEE_X64_H

#include "../onlycpp.h"

#ifdef __SSE4_2_
    #define GEDGEE_SIMD_SSE 42
#elif defined(__SSE4_1__)
    #define GEDGEE_SIMD_SSE 41
#elif defined(__SSE3__)
    #define GEDGEE_SIMD_SSE 30
#elif defined(__SSE2__)
    #define GEDGEE_SIMD_SSE 20
#elif defined(__SSE__)
    #define GEDGEE_SIMD_SSE 10
#else
    #define GEDGEE_SIMD_SSE 00
#endif

#ifdef __SSSE3__
    #define GEDGEE_SIMD_SSSE3
#endif

#if GEDGEE_SIMD_SSE > 0
    #include <smmintrin.h>
#endif

#ifdef __FMA__
    #define GEDGEE_SIMD_FMA
#endif

#if defined(GEDGEE_SIMD_FMA)
    #include <immintrin.h>
#endif

namespace gedgee {
    inline __m128i to_m128i(vector128 v) {
        return *(reinterpret_cast<__m128i*>(&v));
    }

    template <typename T>
    inline __m128 to_m128(vector128 v) {
        return *(reinterpret_cast<__m128*>(&v));
    }

    template <typename T>
    inline __m128d to_m128d(vector128 v) {
        return *(reinterpret_cast<__m128d*>(&v));
    }

    template <typename T>
    inline vector128 to_vector128(T x) {
        return *(reinterpret_cast<vector128*>(&x));
    }
}

namespace gedgee {
    // 转换函数特化
#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_cvt<u8, u16>(vector128 vec) {
        __m128i input = to_m128i(vec);
        __m128i zero = _mm_setzero_si128();
        __m128i result = _mm_unpacklo_epi8(input, zero);
        return to_vector128(result);
    }

    template <>
    inline vector128 v128_cvt<u16, u8>(vector128 vec) {
        __m128i input = to_m128i(vec);
        __m128i result = _mm_packus_epi16(input, input);
        return to_vector128(result);
    }

    template <>
    inline vector128 v128_cvt<i32, f32>(vector128 vec) {
        __m128i input = to_m128i(vec);
        __m128 result = _mm_cvtepi32_ps(input);
        return to_vector128(result);
    }

    template <>
    inline vector128 v128_cvt<f32, i32>(vector128 vec) {
        __m128 input = to_m128<f32>(vec);
        __m128i result = _mm_cvtps_epi32(input);
        return to_vector128(result);
    }
#endif

    // 存储函数特化
#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline void v128_store<u8>(u8 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }

    template <>
    inline void v128_store<u16>(u16 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }

    template <>
    inline void v128_store<u32>(u32 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }

    template <>
    inline void v128_store<u64>(u64 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }

    template <>
    inline void v128_store<i8>(i8 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }

    template <>
    inline void v128_store<i16>(i16 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }

    template <>
    inline void v128_store<i32>(i32 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }

    template <>
    inline void v128_store<i64>(i64 *p, vector128 vec) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(p), to_m128i(vec));
    }
#endif

#if GEDGEE_SIMD_SSE >= 10
    template <>
    inline void v128_store<f32>(f32 *p, vector128 vec) {
        _mm_storeu_ps(p, to_m128<f32>(vec));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline void v128_store<f64>(f64 *p, vector128 vec) {
        _mm_storeu_pd(p, to_m128d<f64>(vec));
    }
#endif

    // 设置函数特化
#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_set<u16>(const u16 n) {
        return to_vector128(_mm_set1_epi16(static_cast<i16>(n)));
    }

    template <>
    inline vector128 v128_set<u32>(const u32 n) {
        return to_vector128(_mm_set1_epi32(static_cast<i32>(n)));
    }

    template <>
    inline vector128 v128_set<u64>(const u64 n) {
        return to_vector128(_mm_set1_epi64x(static_cast<i64>(n)));
    }

    template <>
    inline vector128 v128_set<i16>(const i16 n) {
        return to_vector128(_mm_set1_epi16(n));
    }

    template <>
    inline vector128 v128_set<i32>(const i32 n) {
        return to_vector128(_mm_set1_epi32(n));
    }

    template <>
    inline vector128 v128_set<i64>(const i64 n) {
        return to_vector128(_mm_set1_epi64x(n));
    }
#endif

#if GEDGEE_SIMD_SSE >= 10
    template <>
    inline vector128 v128_set<f32>(const f32 n) {
        return to_vector128(_mm_set1_ps(n));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_set<f64>(const f64 n) {
        return to_vector128(_mm_set1_pd(n));
    }
#endif

    // 算术运算特化
#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_add<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi8(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_add<u16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi16(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_add<u32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_add<u64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi64(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_add<i8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi8(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_add<i16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi16(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_add<i32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_add<i64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_epi64(to_m128i(vec1), to_m128i(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 10
    template <>
    inline vector128 v128_add<f32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_ps(to_m128<f32>(vec1), to_m128<f32>(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_add<f64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_add_pd(to_m128d<f64>(vec1), to_m128d<f64>(vec2)));
    }
#endif

    // 减法运算特化
#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_sub<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi8(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_sub<u16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi16(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_sub<u32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_sub<u64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi64(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_sub<i8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi8(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_sub<i16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi16(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_sub<i32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_sub<i64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_epi64(to_m128i(vec1), to_m128i(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 10
    template <>
    inline vector128 v128_sub<f32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_ps(to_m128<f32>(vec1), to_m128<f32>(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_sub<f64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_sub_pd(to_m128d<f64>(vec1), to_m128d<f64>(vec2)));
    }
#endif

    // 乘法运算特化
#if GEDGEE_SIMD_SSE >= 41
    template <>
    inline vector128 v128_mul<u32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_mullo_epi32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_mul<i32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_mullo_epi32(to_m128i(vec1), to_m128i(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_mul<u16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_mullo_epi16(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_mul<i16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_mullo_epi16(to_m128i(vec1), to_m128i(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 10
    template <>
    inline vector128 v128_mul<f32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_mul_ps(to_m128<f32>(vec1), to_m128<f32>(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_mul<f64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_mul_pd(to_m128d<f64>(vec1), to_m128d<f64>(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 10
    template <>
    inline vector128 v128_div<f32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_div_ps(to_m128<f32>(vec1), to_m128<f32>(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_div<f64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_div_pd(to_m128d<f64>(vec1), to_m128d<f64>(vec2)));
    }
#endif

    // 最大值/最小值运算特化
#if GEDGEE_SIMD_SSE >= 41
    template <>
    inline vector128 v128_max<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_max_epu8(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_max<i16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_max_epi16(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_max<u32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_max_epu32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_max<i32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_max_epi32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_min<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_min_epu8(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_min<i16>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_min_epi16(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_min<u32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_min_epu32(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_min<i32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_min_epi32(to_m128i(vec1), to_m128i(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 42
    // 增强的最大值/最小值操作
    template <>
    inline vector128 v128_max<u64>(vector128 vec1, vector128 vec2) {
        __m128i v1 = to_m128i(vec1);
        __m128i v2 = to_m128i(vec2);
        // 翻转符号位以正确比较无符号数
        const __m128i sign_bit = _mm_set1_epi64x(0x8000000000000000ULL);
        __m128i v1_adj = _mm_xor_si128(v1, sign_bit);
        __m128i v2_adj = _mm_xor_si128(v2, sign_bit);
        // 使用有符号比较调整后的值
        __m128i mask = _mm_cmpgt_epi64(v1_adj, v2_adj);
        return to_vector128(_mm_blendv_epi8(v2, v1, mask));
    }

    template <>
    inline vector128 v128_min<u64>(vector128 vec1, vector128 vec2) {
        __m128i v1 = to_m128i(vec1);
        __m128i v2 = to_m128i(vec2);
        // 翻转符号位以正确比较无符号数
        const __m128i sign_bit = _mm_set1_epi64x(0x8000000000000000ULL);
        __m128i v1_adj = _mm_xor_si128(v1, sign_bit);
        __m128i v2_adj = _mm_xor_si128(v2, sign_bit);
        // 使用有符号比较调整后的值
        __m128i mask = _mm_cmpgt_epi64(v1_adj, v2_adj);
        // 注意：混合顺序与max相反
        return to_vector128(_mm_blendv_epi8(v1, v2, mask));
    }
#endif

#if GEDGEE_SIMD_SSE >= 10
    template <>
    inline vector128 v128_max<f32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_max_ps(to_m128<f32>(vec1), to_m128<f32>(vec2)));
    }

    template <>
    inline vector128 v128_min<f32>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_min_ps(to_m128<f32>(vec1), to_m128<f32>(vec2)));
    }
#endif

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_max<f64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_max_pd(to_m128d<f64>(vec1), to_m128d<f64>(vec2)));
    }

    template <>
    inline vector128 v128_min<f64>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_min_pd(to_m128d<f64>(vec1), to_m128d<f64>(vec2)));
    }
#endif

    // 位运算特化
#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_and<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_and_si128(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_or<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_or_si128(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_xor<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_xor_si128(to_m128i(vec1), to_m128i(vec2)));
    }

    template <>
    inline vector128 v128_andnot<u8>(vector128 vec1, vector128 vec2) {
        return to_vector128(_mm_andnot_si128(to_m128i(vec1), to_m128i(vec2)));
    }
#endif

    // 移位运算特化
#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_sll<u16>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_slli_epi16(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_sll<u32>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_slli_epi32(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_sll<u64>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_slli_epi64(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_sll<i16>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_slli_epi16(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_sll<i32>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_slli_epi32(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_sll<i64>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_slli_epi64(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_srl<u16>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_srli_epi16(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_srl<u32>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_srli_epi32(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_srl<u64>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_srli_epi64(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_sra<i16>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_srai_epi16(to_m128i(vec), count));
    }

    template <>
    inline vector128 v128_sra<i32>(vector128 vec, uint32_t count) {
        return to_vector128(_mm_srai_epi32(to_m128i(vec), count));
    }
#endif

    template <>
    inline vector128 v128_abs<f32>(vector128 vec) {
        const vector128 mask = constant::v128_fill<u32>(0x7FFFFFFF);
        return v128_and<uint32_t>(vec, mask);
    }

    template <>
    inline vector128 v128_neg<f32>(vector128 vec) {
        vector128 output = v128_fill<uint32_t>(0x7FFFFFFF);
        return v128_xor<uint32_t>(vec, output);
    }

    // 数学函数特化
#if GEDGEE_SIMD_SSE >= 10

    template <>
    inline vector128 v128_sqrt<f32>(vector128 vec) {
        return to_vector128(_mm_sqrt_ps(to_m128<f32>(vec)));
    }

    template <>
    inline vector128 v128_rsqrt<f32>(vector128 vec) {
        return to_vector128(_mm_rsqrt_ps(to_m128<f32>(vec)));
    }

    template <>
    inline vector128 v128_rcp<f32>(vector128 vec) {
        return to_vector128(_mm_rcp_ps(to_m128<f32>(vec)));
    }
#endif

    template <>
    inline vector128 v128_abs<f64>(vector128 vec) {
        const vector128 mask = constant::v128_fill<u64>(0x7FFFFFFFFFFFFFFF);
        return v128_and<uint64_t>(vec, mask);
    }

    template <>
    inline vector128 v128_neg<f64>(vector128 vec) {
        vector128 output = v128_fill<uint64_t>(0x7FFFFFFFFFFFFFFF);
        return v128_xor<uint64_t>(vec, output);
    }

#if GEDGEE_SIMD_SSE >= 20
    template <>
    inline vector128 v128_sqrt<f64>(vector128 vec) {
        return to_vector128(_mm_sqrt_pd(to_m128d<f64>(vec)));
    }

    template <>
    inline vector128 v128_rsqrt<f64>(vector128 vec) {
        return to_vector128(_mm_div_pd(_mm_set1_pd(1.0), _mm_sqrt_pd(to_m128d<f64>(vec))));
    }
#endif

#if defined(GEDGEE_SIMD_SSSE3) || GEDGEE_SIMD_SSE >= 41
    template <>
    inline vector128 v128_abs<i8>(vector128 vec) {
        return to_vector128(_mm_abs_epi8(to_m128i(vec)));
    }

    template <>
    inline vector128 v128_abs<i16>(vector128 vec) {
        return to_vector128(_mm_abs_epi16(to_m128i(vec)));
    }

    template <>
    inline vector128 v128_abs<i32>(vector128 vec) {
        return to_vector128(_mm_abs_epi32(to_m128i(vec)));
    }
#endif

    template <>
    inline vector128 v128_abs<i64>(vector128 vec) {
        const vector128 mask = v128_sra<i64>(vec, 63);
        const vector128 temp = v128_add<i64>(vec, mask);
        return v128_xor<int64_t>(temp, mask);
    }

    // 舍入函数特化
#if GEDGEE_SIMD_SSE >= 41
    template <>
    inline vector128 v128_round<f32>(vector128 vec) {
        return to_vector128(_mm_round_ps(to_m128<f32>(vec), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));
    }

    template <>
    inline vector128 v128_round<f64>(vector128 vec) {
        return to_vector128(_mm_round_pd(to_m128d<f64>(vec), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));
    }
#endif

#if defined(GEDGEE_SIMD_FMA)
    template <>
    inline vector128 v128_fma<f32>(vector128 vec, vector128 fmul, vector128 vadd) {
        return to_vector128(_mm_fmadd_ps(to_m128<f32>(vec), to_m128<f32>(fmul), to_m128<f32>(vadd)));
    }

    template <>
    inline vector128 v128_fma<f64>(vector128 vec, vector128 fmul, vector128 vadd) {
        return to_vector128(_mm_fmadd_pd(to_m128d<f64>(vec), to_m128d<f64>(fmul), to_m128d<f64>(vadd)));
    }
#endif

}

#endif
